package com.threegroup.admin.security.filter;

import com.threegroup.admin.security.jwt.Audience;
import com.threegroup.admin.security.jwt.JwtTokenUtil;
import com.threegroup.common.constant.Constant;
import com.threegroup.common.redis.RedisConf;
import com.threegroup.common.redis.RedisUtils;
import com.threegroup.common.utils.StringUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Resource
    private Audience audience;

    @Resource
    private JwtTokenUtil jwtTokenUtil;

    @Resource
    private UserDetailsService userDetailsService;

    @Resource
    private RedisUtils redisUtils;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {

        // 得到请求头中authentication信息
        String authHeader = request.getHeader(Constant.TOKEN_HEADER);

        if (StringUtils.isEmpty(authHeader)) {
            Cookie[] cookies = request.getCookies();
            if (!StringUtils.isEmpty(cookies)) {
                List<Cookie> cookieList = Arrays.stream(cookies).filter(
                        cookie -> "token".equals(cookie.getName())
                ).collect(Collectors.toList());
                if (!StringUtils.isEmpty(cookieList.toArray())) {
                    authHeader = cookieList.get(0).getValue();
                }
            }
        }

        if (authHeader != null && authHeader.startsWith(Constant.TOKEN_HEADER)) {

            final String token = authHeader.substring(Constant.TOKEN_HEADER.length());
            // 私钥
            String base64Secret = audience.getBase64Secret();

            // 查询缓存中 token
            String onlineUser = (String) redisUtils.get(String.format("%s:%s", RedisConf.LOGIN_TOKEN_KEY, authHeader));

            // 当缓存失效，则认为现在 token 是过期 token
            if (StringUtils.isEmpty(onlineUser)) {
                logger.error("[JwtTokenFilter] 用户token已过期");
                chain.doFilter(request, response);
                return;
            }

            // 解析token的 id
            String id = null;
            String username = "";

            try {
                id = jwtTokenUtil.getUserUid(token, base64Secret);
                username = jwtTokenUtil.getUsername(token, base64Secret);
            } catch (Exception e) {
                logger.error("[JwtTokenFilter] 用户token校验失败");
                chain.doFilter(request, response);
                return;
            }

            request.setAttribute(Constant.TOKEN_HEADER, token);
            request.setAttribute(Constant.USER_NAME, username);
            request.setAttribute(Constant.USER_ID, id);

            logger.info("[JwtTokenFilter] 用户名: " + username + ", 用户id: " + id);

            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                // 通过用户名加载 SpringSecurity 用户
                UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
                // 验证Token 有效性
                if (jwtTokenUtil.validateToken(token, userDetails, base64Secret)) {
                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                            userDetails, null, userDetails.getAuthorities());
                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    // 以后可以security中取得 SecurityUser 信息
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            }
        }

        chain.doFilter(request, response);
    }
}
